//如果一个对象中有堆区资源，需要编写拷贝构造函数和赋值函数，实现深拷贝。
//深拷贝把对象中的堆区资源复制了一份，如果源对象(被拷贝的对象)是临时对象，拷贝完就没什么用了，这样会造成没有意义的资源申请和释放操作。
//如果能够直接使用源对象拥有的资源，可以节省资源申请和释放的时间。C++11新增加的移动语义就能够做到这一点。

//实现移动语义要增加两个函数 : 移动构造函数和移动赋值函数。

//类名(类名&& 源对象){...};		移动构造函数语法
//类名& operator=(类名&& 源对象){...}	移动赋值函数语法(重载=)

#include<iostream>
#include<functional>
using namespace std;

class AA
{
public:
	int* m_data = nullptr;		//数据成员，指向堆区资源的指针
	
	AA() = default;		//启用默认构造函数

	void alloc()		//给数据成员m_data分配内存
	{
		m_data = new int;		//分配内存
		memset(m_data, 0, sizeof(int));	//初始化已分配的内存
	}

	AA(const AA& a)		//拷贝构造函数
	{
		cout << "调用了拷贝构造函数" << endl;
		if (m_data == nullptr) alloc();		//如果没有分配内存，就分配
		memcpy(m_data, a.m_data, sizeof(int));	//把数据从源对象中拷贝过来
	}

	AA& operator=(const AA& a)	//赋值函数
	{
		cout << "调用了赋值函数。" << endl;
		if (this == &a)	return *this;	//避免自我赋值
		if (m_data == nullptr) alloc();	
		memcpy(m_data, a.m_data, sizeof(int));
		return *this;
	}

	~AA()	//析构函数
	{
		if (m_data != nullptr)
		{
			delete m_data;
			m_data = nullptr;
		}
	}
};

int main()
{
	AA a1;
	a1.alloc();
	*a1.m_data = 3;		//给堆区内存赋值
	cout << "a1.m_data = " << *a1.m_data << endl;

	AA a2 = a1;		//调用拷贝构造
	cout << "a2m_data = " << *a2.m_data << endl;

	AA a3;
	a3 = a1;	//调用赋值函数
	cout << "a3.m_data = " << *a3.m_data << endl;

	return 0;
}

/*
a1.m_data = 3
调用了拷贝构造函数
a2m_data = 3
调用了赋值函数。
a3.m_data = 3
*/
//以上就是拷贝语义，每次调用赋值函数或者拷贝函数，对象中堆区资源都会复制一次，很浪费时间和资源
//移动语义就是不需要拷贝，直接将资源转移过来，资源都是由指针指向的，处理一下指针即可转移资源



class AA
{
public:
	int* m_data = nullptr;		

	AA() = default;
	

	void alloc()		//分配内存函数
	{
		m_data = new int;		
		memset(m_data, 0, sizeof(int));	
	}

	AA(const AA& a)		//拷贝构造函数
	{
		cout << "调用了拷贝构造函数" << endl;
		if (m_data == nullptr) alloc();		
		memcpy(m_data, a.m_data, sizeof(int));	
	}

	AA(AA&& a)		//添加移动构造函数（不能用const，下面需要操作a）
	{	//（参数改为右值引用）
		cout << "调用了移动构造函数。" << endl;
		if (m_data != nullptr) delete m_data;		//如果已分配内存，先释放掉
		m_data = a.m_data;		//把资源从源对象中转移过来,指针指向新的资源
		a.m_data = nullptr;		//源对象中指针置空
	}

	AA& operator=(const AA& a)	//赋值函数
	{
		cout << "调用了赋值函数。" << endl;
		if (this == &a)	return *this;	
		if (m_data == nullptr) alloc();
		memcpy(m_data, a.m_data, sizeof(int));
		return *this;
	}

	AA& operator=( AA&& a)	//添加移动赋值函数（参数改为右值引用）
	{
		cout << "调用了移动赋值函数。" << endl;
		if (this == &a)	return *this;
		if (m_data != nullptr) delete m_data;		//如果已分配内存，先释放掉
		m_data = a.m_data;		//把资源从源对象中转移过来,指针指向新的资源
		a.m_data = nullptr;		//源对象中指针置空
		return *this;
	}

	~AA()	
	{
		if (m_data != nullptr)
		{
			delete m_data;
			m_data = nullptr;
		}
	}
};


int main()
{
	AA a1;
	a1.alloc();
	*a1.m_data = 3;		//给堆区内存赋值
	cout << "a1.m_data = " << *a1.m_data << endl << endl;

	AA a2(a1);		//调用拷贝构造
	cout << "a2.m_data = " << *a2.m_data << endl << endl;

	AA a3;
	a3 = a1;	//调用赋值函数
	cout << "a3.m_data = " << *a3.m_data << endl << endl;

	//调用移动构造函数和赋值函数的方式很简单，参数是右值即可，参数是左值就是调用拷贝构造

	function<AA()> f = []			//返回AA类对象的lambda函数
	{
		AA aa;
		aa.alloc();
		*aa.m_data = 8;
		return aa;			
	};

	AA a4(f());		//lambda函数返回临时对象是右值，调用移动构造函数
	cout << "a4.m_data = " << *a4.m_data << endl<<endl;

	AA a5;
	a5 = f();	//调用移动赋值函数
	cout << "a5.m_data = " << *a5.m_data << endl << endl;


	//move()可将左值和右值相互转换
	AA a6 = move(a1);
	cout << "a6.m_data = " << *a6.m_data << endl << endl;

	AA a7;
	a7 = move(f());
	cout << "a7.m_data = " << *a7.m_data << endl << endl;
	//注意不要对同一个值连续进行move转换，否则容易出现空指针，程序崩溃
	return 0;
}
/*

a1.m_data = 3

调用了拷贝构造函数
a2.m_data = 3

调用了赋值函数。
a3.m_data = 3

调用了移动赋值函数。
a4.m_data = 8

调用了移动赋值函数。
a5.m_data = 8

调用了移动构造函数。
a6.m_data = 3

调用了移动赋值函数。
a7.m_data = 8
*/

//移动语义对于拥有资源(如内存，文件句柄)的对象有效，如果是基本类型，使用移动语义没有意义